﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DBSourceToolsLib.Suggestions;

namespace ICSharpCode.MSSQLExtensions.SqlParser
{
    public class TextWordParser
    {
        LinkedList<SqlWord> m_listSqlWords = new LinkedList<SqlWord>();

        HashSet<string> m_hashTableNames = new HashSet<string>();
        HashSet<string> m_hashFieldNames = new HashSet<string>();
        HashSet<string> m_hashViewNames = new HashSet<string>();

        public void SetTableNameList(List<string> lstTables)
        {
            m_hashTableNames.Clear();
            foreach (string strTableName in lstTables)
                m_hashTableNames.Add(strTableName.ToLower());
        }

        public void SetFieldNameList(List<string> lstFields)
        {
            m_hashFieldNames.Clear();
            foreach (string strFieldName in lstFields)
                m_hashFieldNames.Add(strFieldName.ToLower());
        }


        public void AddTextWord(ICSharpCode.TextEditor.Document.TextWord oTextWord, long lLineSegmentOffset)
        {
            SqlWord oCurrentWord = new SqlWord { TextWord = oTextWord, SqlType = SqlType.Unknown, LineSegmentOffset = lLineSegmentOffset };
            string strCompareText = oTextWord.Word.ToLower();
            switch (strCompareText)
            {
                case "select": oCurrentWord.SqlType = SqlType.Select; break;
                case "from": oCurrentWord.SqlType = SqlType.From; break;
                case "and": oCurrentWord.SqlType = SqlType.And; break;
                case "begin": oCurrentWord.SqlType = SqlType.Begin; break;
                case "end": oCurrentWord.SqlType = SqlType.End; break;
                case "case": oCurrentWord.SqlType = SqlType.Case; break;
                case "join": oCurrentWord.SqlType = SqlType.Join; break;
                case "on": oCurrentWord.SqlType = SqlType.On; break;
                case "inner": oCurrentWord.SqlType = SqlType.Inner; break;
                case "where": oCurrentWord.SqlType = SqlType.Where; break;
                case "group": oCurrentWord.SqlType = SqlType.Group; break;
                case "order": oCurrentWord.SqlType = SqlType.Order; break;
                case "as": oCurrentWord.SqlType = SqlType.As; break;
                case "--": oCurrentWord.SqlType = SqlType.SingleLineComment; break;
                case "/*": oCurrentWord.SqlType = SqlType.StartComment; break;
                case "*/": oCurrentWord.SqlType = SqlType.EndComment; break;
                case "(": oCurrentWord.SqlType = SqlType.StartBrace; break;
                case ")": oCurrentWord.SqlType = SqlType.EndBrace; break;
                case ".": oCurrentWord.SqlType = SqlType.Dot; break;
            }

            if (oCurrentWord.SqlType == SqlType.Unknown)
            {
                CheckForTables(ref oCurrentWord, strCompareText);
            }

            if (oCurrentWord.SqlType == SqlType.Unknown)
            {
                CheckForViews(ref oCurrentWord, strCompareText);
            }

            if (oCurrentWord.SqlType == SqlType.Unknown)
            {
                // check for fields
                if (m_hashFieldNames.Contains(strCompareText))
                    oCurrentWord.SqlType = SqlType.Field;
            }

            if (m_listSqlWords.Count == 0)
                m_listSqlWords.AddFirst(oCurrentWord);
            else
                m_listSqlWords.AddLast(oCurrentWord);

        }

        private void CheckForTables(ref SqlWord oCurrentWord, string strCompareText)
        {
            // check for tables 
            if (m_hashTableNames.Contains(strCompareText))
                oCurrentWord.SqlType = SqlType.Table;

            // check for tablenames with schema i.e. SalesLT.Address
            if (oCurrentWord.SqlType == SqlType.Unknown)
            {
                if ((m_listSqlWords.Last != null) && (m_listSqlWords.Last.Value.SqlType == SqlType.Dot) && (m_listSqlWords.Last.Previous != null))
                {
                    LinkedListNode<SqlWord> oPrevWord = m_listSqlWords.Last.Previous;
                    string strSchemaTable = oPrevWord.Value.TextWord.Word.ToLower() + "." + oCurrentWord.TextWord.Word.ToLower();
                    if (m_hashTableNames.Contains(strSchemaTable))
                        oCurrentWord.SqlType = SqlType.TableWithSchema;
                }
            }
        }

        private void CheckForViews(ref SqlWord oCurrentWord, string strCompareText)
        {
            // check for tables 
            if (m_hashViewNames.Contains(strCompareText))
                oCurrentWord.SqlType = SqlType.View;

            // check for tablenames with schema i.e. SalesLT.Address
            if (oCurrentWord.SqlType == SqlType.Unknown)
            {
                if ((m_listSqlWords.Last != null) && (m_listSqlWords.Last.Value.SqlType == SqlType.Dot) && (m_listSqlWords.Last.Previous != null))
                {
                    LinkedListNode<SqlWord> oPrevWord = m_listSqlWords.Last.Previous;
                    string strFullWord = oPrevWord.Value.TextWord.Word.ToLower() + "." + oCurrentWord.TextWord.Word.ToLower();
                    if (m_hashViewNames.Contains(strFullWord))
                        oCurrentWord.SqlType = SqlType.ViewWithSchema;
                }
            }
        }


        public string GetDebugString()
        {
            StringBuilder oBuilder = new StringBuilder();

            LinkedListNode<SqlWord> oNode = m_listSqlWords.First;
            while (oNode != null)
            {
                oBuilder.AppendLine(string.Format(" {0}:{1} {4} {2}-{3} {5}", oNode.Value.TextWord.Word, oNode.Value.SqlType, oNode.Value.TextWord.Offset, oNode.Value.TextWord.Length, oNode.Value.LineSegmentOffset
                    , oNode.Value.FullOffset));

                oNode = oNode.Next;

            }
            return oBuilder.ToString();


        }

        public LinkedListNode<SqlWord> GetCurrentNode(int iOffset)
        {
            LinkedListNode<SqlWord> oNode = m_listSqlWords.First;
            while (oNode != null)
            {
                if (iOffset > (oNode.Value.FullOffset))
                {
                    if (oNode.Next != null)
                    {
                        if (iOffset < oNode.Next.Value.FullOffset)
                            return oNode;
                    }
                    else
                    {
                        return oNode;
                    }
                }

                oNode = oNode.Next;

            }
            return oNode;

        }

        public bool IsAfterWord(int iOffset)
        {
            LinkedListNode<SqlWord> oCurrentNode = GetCurrentNode(iOffset);

            //if (oCurrentNode != null)
            //{
            //    HCSocketToolsLib.HCSocketSender.Instance().SendUDPMessage(
            //        System.Reflection.MethodBase.GetCurrentMethod()
            //        , "CurrentNode : " + oCurrentNode.Value.TextWord.Word);
            //}


            return (oCurrentNode != null);
        }


        public string PrevWord(int iOffset)
        {
            LinkedListNode<SqlWord> oPrevWordNode = GetCurrentNode(iOffset);
            if (oPrevWordNode == null)
                return "";
            else
                return oPrevWordNode.Value.TextWord.Word;
        }

        internal bool IsInSelect(int iCursorOffset, out LinkedListNode<SqlWord> oSelectNode)
        {
            oSelectNode = null;
            LinkedListNode<SqlWord> oCurrentNode = GetCurrentNode(iCursorOffset);

            if (oCurrentNode == null)
                return false;

            while (oCurrentNode != null)
            {
                if (oCurrentNode.Value.SqlType == SqlType.Select)
                {
                    oSelectNode = oCurrentNode;
                    {
                        HCSocketToolsLib.HCSocketSender.Instance().SendUDPMessage(
                            System.Reflection.MethodBase.GetCurrentMethod()
                            , "IsInSelect " + oCurrentNode.Value.TextWord.Word);

                        return true;
                    }
                }
                oCurrentNode = oCurrentNode.Previous;
            }
            return false;
        }

        internal List<TableAndAlias> FindTablesAndAliases(LinkedListNode<SqlWord> oSelectNode)
        {
            List<TableAndAlias> lstNames = new List<TableAndAlias>();

            LinkedListNode<SqlWord> oCurrentNode = oSelectNode;

            if (oCurrentNode == null)
                return lstNames;

            while (oCurrentNode != null)
            {
                SqlType oCurrentType = oCurrentNode.Value.SqlType;
                if (oCurrentType == SqlType.Table || oCurrentType == SqlType.TableWithSchema
                    || oCurrentType == SqlType.View || oCurrentType == SqlType.ViewWithSchema
                    )

                {
                    string strFullTableName = oCurrentNode.Value.TextWord.Word;
                    if (oCurrentType == SqlType.TableWithSchema || oCurrentType == SqlType.ViewWithSchema)
                    {
                        strFullTableName = oCurrentNode.Previous.Previous.Value.TextWord.Word + "." + strFullTableName;
                    }
                    if (oCurrentNode.Next != null)
                    {   // check for an alias
                        if (oCurrentNode.Next.Value.SqlType == SqlType.Unknown)
                        {
                            lstNames.Add(new TableAndAlias
                            {
                                TableName = strFullTableName,
                                AliasName = oCurrentNode.Next.Value.TextWord.Word
                            });
                        }
                        else
                        {
                            if (oCurrentNode.Next.Value.SqlType == SqlType.As)
                            {
                                if (oCurrentNode.Next.Next != null)
                                {
                                    if (oCurrentNode.Next.Next.Value.SqlType == SqlType.Unknown)
                                        lstNames.Add(new TableAndAlias
                                        {
                                            TableName = strFullTableName,
                                            AliasName = oCurrentNode.Next.Next.Value.TextWord.Word
                                        });
                                }
                            } else
                                lstNames.Add(new TableAndAlias { TableName = strFullTableName });
                        }
                    }
                    else
                    {
                        lstNames.Add(new TableAndAlias { TableName = strFullTableName });
                    }

                }
                if ((oCurrentType == SqlType.Where) || (oCurrentType == SqlType.Group)
                    || (oCurrentType == SqlType.Order))

                    break;

                oCurrentNode = oCurrentNode.Next;
                if ((oCurrentNode != null) && (oCurrentNode.Value.SqlType == SqlType.Select))
                    break;

            }


            return lstNames;
        }

        internal bool IsAfterField(int iCursorOffset)
        {
            LinkedListNode<SqlWord> oCurrentNode = GetCurrentNode(iCursorOffset);

            if (oCurrentNode == null)
                return false;
            if (oCurrentNode.Value.SqlType == SqlType.Field)
            {
                HCSocketToolsLib.HCSocketSender.Instance().SendUDPMessage(
                    System.Reflection.MethodBase.GetCurrentMethod()
                    , "IsAfterField " + oCurrentNode.Value.TextWord.Word);

                return true;
            }

            return false;
        }

        internal bool IsAfterFrom(int iCursorOffset)
        {
            LinkedListNode<SqlWord> oCurrentNode = GetCurrentNode(iCursorOffset);

            if (oCurrentNode == null)
                return false;

            while (oCurrentNode != null)
            {
                SqlType oCurrentType = oCurrentNode.Value.SqlType;
                switch (oCurrentType)
                {
                    case SqlType.From:
                    case SqlType.Join:
                        {
                            HCSocketToolsLib.HCSocketSender.Instance().SendUDPMessage(System.Reflection.MethodBase.GetCurrentMethod()
                                , "IsAfterFrom " + oCurrentNode.Value.TextWord.Word);

                            return true;
                        }
                    case SqlType.Where:
                    case SqlType.Group:
                    case SqlType.Order:
                    case SqlType.On:
                        return false;

                }

                oCurrentNode = oCurrentNode.Previous;
            }

            return false;

        }



        internal void ClearWords()
        {
            m_listSqlWords.Clear();
        }

        internal bool IsAfterTableOrAlias(int iCursorOffset)
        {
            LinkedListNode<SqlWord> oCurrentNode = GetCurrentNode(iCursorOffset);

            if (oCurrentNode == null)
                return false;

            if (oCurrentNode.Value.SqlType == SqlType.Where || oCurrentNode.Value.SqlType == SqlType.Order || oCurrentNode.Value.SqlType == SqlType.Group
                || oCurrentNode.Value.SqlType == SqlType.Join || oCurrentNode.Value.SqlType == SqlType.On
                )
                return false;

            if ((oCurrentNode.Value.SqlType == SqlType.Table) || (oCurrentNode.Value.SqlType == SqlType.View)
                || (oCurrentNode.Value.SqlType == SqlType.TableWithSchema)
                || (oCurrentNode.Value.SqlType == SqlType.ViewWithSchema)
                )
            {
                HCSocketToolsLib.HCSocketSender.Instance().SendUDPMessage(System.Reflection.MethodBase.GetCurrentMethod()
                    , "IsAfterTableOrAlias " + oCurrentNode.Value.TextWord.Word);
                return true;
            }



            if (oCurrentNode.Previous != null &&
                (oCurrentNode.Previous.Value.SqlType == SqlType.Table || oCurrentNode.Previous.Value.SqlType == SqlType.TableWithSchema))
            {
                HCSocketToolsLib.HCSocketSender.Instance().SendUDPMessage(System.Reflection.MethodBase.GetCurrentMethod()
                    , "IsAfterTableOrAlias " + oCurrentNode.Value.TextWord.Word);

                return true;
            }

            if (oCurrentNode.Previous != null &&
                (oCurrentNode.Previous.Value.SqlType == SqlType.View || oCurrentNode.Previous.Value.SqlType == SqlType.ViewWithSchema))
            {
                HCSocketToolsLib.HCSocketSender.Instance().SendUDPMessage(System.Reflection.MethodBase.GetCurrentMethod()
                    , "IsAfterTableOrAlias" + oCurrentNode.Value.TextWord.Word);

                return true;
            }


            return false;

        }

        internal void SetViewNameList(List<string> list)
        {
            m_hashViewNames.Clear();
            foreach (string strFieldName in list)
                m_hashViewNames.Add(strFieldName.ToLower());

        }
    }
}
